/*
 *  Copyright 2010 Wesley Gooch, Ryan Nix, Cody Wenzel.
 *  Montana State University - Spring Semester 2010
 *  Computer Science 450 - Compilers
 *  Project: Micro-Pascal Compiler
 */

package msu.compiler.scanner.fsas;

import java.util.HashMap;
import msu.compiler.scanner.Token;
import msu.compiler.scanner.Tokenizer;

/**
 * Abstract Base Class for All FSA StateMachines. Every State Machine must
 * extend this class in order to function properly with the Lexical Scanner.
 * 
 * @author Wesley Gooch
 */
public abstract class FSA {
    
    private Tokenizer tokenizer;
    private HashMap<String, Integer> states = new HashMap<String, Integer>();
    private Integer currentState;
    private String lastAcceptState = null;
    private byte lastAcceptedTokenType;
    private Boolean done = false;

    protected FSA(Tokenizer tokenizer) {
        this.tokenizer = tokenizer;
    }

    /**
     * All subclasses must implement the execute method.
     */
    protected abstract void execute();

    // ------------------------------------------------------------------
    // State & States Management
    // ------------------------------------------------------------------

    /**
     * Initializes the states HashMap with the number of states provided
     * in the numStates parameter. Each state's key/name will start with s0
     * and increment up to numStates - 1. The currentState is also
     * initialized to point at s0. The start state s0 is returned.
     * 
     * @param   numStates The number of states that needs to be added to the
     *                    states HashMap. Initilized to s0 up-to (numStates-1)
     * @return  The value of the currentState which is initilized to the start
     *          state of s0. An Integer of value 0.
     */
    protected Integer initializeStates(int numStates) {
        for(int i=0; i<numStates; i++) {
            states.put(("s" + i), new Integer(i));
        }
        setCurrentState("s0");
        return getCurrentState();
    }

    protected Integer getCurrentState() {
        return currentState;
    }

    protected char getCurrentChar() {
        return tokenizer.getCurrentChar();
    }

    protected void peekAhead() {
        tokenizer.peekAhead();
    }

    protected Boolean notFinished() {
        return !done;
    }

    protected void finish() {
        done = true;
    }

    /**
     * Sets the current state to the given String state key. Example: "s0".
     * The key is autmatically looked up in the states {@link HashMap} and the
     * found state's value is set to the currentState.
     *
     * @param stateKey  A string representation of the current state's
     *                      key, such as "s0" or "s5".
     */
    protected void setCurrentState(String stateKey) {
        currentState = states.get(stateKey);
        if(tokenizer.isAtEOF()) finish(); // Check for EOF after each state!
    }
    
    protected void setAcceptState(String stateKey, byte tokenType) {
        setCurrentState(stateKey);
        lastAcceptState = stateKey;
        lastAcceptedTokenType = tokenType;
        tokenizer.setMark();
        tokenizer.setCurrentTokenType(tokenType);
    }

    protected void fail() {
        if(lastAcceptState == null) {
            tokenizer.setCurrentTokenType(Token.MP_ERROR);
        } else {
            tokenizer.setCurrentTokenType(lastAcceptedTokenType);
        }
        tokenizer.revertToMark();
        finish();
    }

    protected void handleError(byte errorType) {
        tokenizer.setCurrentTokenType(errorType);
        finish();
    }
}